package edu.gatech.oad.yuno;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.location.Address;
import android.location.Geocoder;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

/**
 * @author michael
 *
 */
public class Location extends Model implements Comparable<Location> {
	
	public Location(long id) {
		if (Model.context == null) Model.context = YUNoDosApplication.getAppContext();
		this.id = id;
		this.table_name = "locations";
		this.data = new HashMap<String,Object>();
		this.load();
	}
	
	public Location() {
		this(0);
	}
	
	public void validate() throws ValidationException {
		if (getName() == null || getName().equals(""))
			throw new ValidationException("Name cannot be blank.");
		if (getUser() == null || getUser().getId() == 0)
			throw new ValidationException("No user.");
		if (getAddress() == null || getAddress().equals(""))
			throw new ValidationException("Address cannot be blank.");
		if (!isNetworkAvailable())
			throw new ValidationException("No internet connection.");
		Geocoder gc = new Geocoder(Model.context);
		List<Address> addresses;
		try {
			addresses = gc.getFromLocationName(getAddress(), 2);
		} catch (IOException e) {
			throw new ValidationException("Could not look up address.");
		}
		if (addresses.isEmpty()) throw new ValidationException("Address not found.");
		if (addresses.size() > 1) throw new ValidationException("Ambiguous address.");
		try {
			double longitude = addresses.get(0).getLongitude();
		} catch (IllegalArgumentException e) {
			throw new ValidationException("Address not found.");
		}
	}
	
	public boolean save() {
		Geocoder gc = new Geocoder(Model.context);
		List<Address> addresses;
		try {
			addresses = gc.getFromLocationName(getAddress(), 2);
		} catch (IOException e) {
			return false;
		}
		if (addresses.size() != 1) return false;
		
		try {
			setLongitude(addresses.get(0).getLongitude());
			setLatitude(addresses.get(0).getLatitude());
		} catch (IllegalArgumentException e) {
			return false;
		}
		
		return super.save();
	}
	
	/**
	 * Find a Location in the database by id
	 * @param id
	 * @return the Location if found, null if not found
	 */
	public static Location find(long id) {
		if (Model.context == null) Model.context = YUNoDosApplication.getAppContext();
		TodoListOpenHelper helper = new TodoListOpenHelper(Model.context);
		SQLiteDatabase db = helper.getReadableDatabase();
		String cols[] = {"id"};
		
		Cursor c = db.query("locations", cols, "id="+id, null, null, null, null, "1");
		if (c == null || c.getCount() < 1) {
			db.close();
			return null;
		}
		c.moveToFirst();
		long foundId = c.getLong(c.getColumnIndex("id"));
		
		db.close();
		return new Location(foundId);
	}
	
	public static List<Location> findAllByUser(long userId) {
		if (Model.context == null) Model.context = YUNoDosApplication.getAppContext();
		List<Location> list = new ArrayList<Location>();
		TodoListOpenHelper helper = new TodoListOpenHelper(Model.context);
		SQLiteDatabase db = helper.getReadableDatabase();
		String cols[] = {"id"};
		
		Cursor c = db.query("locations", cols, "user_id="+userId, null, null, null, null, null);
		if (c == null || c.getCount() < 1) {
			db.close();
			return list;
		}
		c.moveToFirst();
		list.add(new Location(c.getLong(c.getColumnIndex("id"))));
		while (!c.isLast()) {
			c.moveToNext();
			list.add(new Location(c.getLong(c.getColumnIndex("id"))));
		}
		Collections.sort(list);
		
		db.close();
		return list;
	}
	/**
	 * 
	 * @return the user
	 */
	public User getUser() {
		long id = getLong("user_id");
		if (id < 1) return null;
		return new User(id);
	}
	
	/**
	 * 
	 * @param user the user to set
	 */
	public void setUser(User user) {
		if (user == null) data.put("user_id", null);
		else data.put("user_id", Long.toString(user.getId()));
	}
	
	/**
	 * 
	 * @param userId the user id to set
	 */
	public void setUser(long userId) {
		data.put("user_id", Long.toString(userId));
	}
	
	/**
	 * @return the name
	 */
	public String getName() {
		return (String) data.get("name");
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		data.put("name", name);
	}

	/**
	 * @return the address
	 */
	public String getAddress() {
		return (String) data.get("address");
	}

	/**
	 * @param address the address to set
	 */
	public void setAddress(String address) {
		data.put("address", address);
	}

	/**
	 * @return the latitude
	 */
	public double getLatitude() {
		return getDouble("latitude");
	}

	/**
	 * @param latitude the latitude to set
	 */
	public void setLatitude(double latitude) {
		data.put("latitude", Double.toString(latitude));
	}

	/**
	 * @return the longitude
	 */
	public double getLongitude() {
		return getDouble("longitude");
	}

	/**
	 * @param longitude the longitude to set
	 */
	public void setLongitude(double longitude) {
		data.put("longitude", Double.toString(longitude));
	}
	
	@Override
	public String toString() {
		return getName();
	}

	@Override
	public int compareTo(Location another) {
		return getName().compareTo(another.getName());
	}
	
	/**
	 * @return The number of uncompleted Todo's at this location.
	 */
	public int numberOfTodos() {
		if (this.id == 0) return 0;
		return SearchProvider.excludeCompleted(SearchProvider.filter(
				getUser(), 0, this, null, null, null)).size();
	}
	
	private boolean isNetworkAvailable() {
	    ConnectivityManager connectivityManager 
	          = (ConnectivityManager) Model.context.getSystemService(Context.CONNECTIVITY_SERVICE);
	    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
	    return activeNetworkInfo != null;
	}
}
